package com.dinghui.pay.controller.union;

import com.dinghui.pay.common.ResponseResult;
import com.dinghui.pay.pojo.TbOrder;
import com.dinghui.pay.pojo.TbUser;
import com.dinghui.pay.pojo.WapUnionPay;
import com.dinghui.pay.pojo.param.PayOrderParam;
import com.dinghui.pay.service.OrderService;
import com.dinghui.pay.service.UserService;
import com.dinghui.pay.utils.JwtUtils;
import com.dinghui.pay.utils.RedisUtils;
import com.unionpay.acp.sdk.AcpService;
import com.unionpay.acp.sdk.SDKConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.Writer;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @version 1.0
 * @author: trt
 * @description: 安卓APP的银联支付业务接口
 * @create: 2021-06-06 11:49
 */
@RestController
@RequestMapping("/api/unionpay")
public class AppUnionPayController {

    /**
     * 开通银联支付业务
     *
     * 注册开发者账号
     * (1)使用银联支付业务接口之前，我们必须要先注册成为银联开发者
     * (2)https://merchant.unionpay.com/cas/register.do  https://open.unionpay.com/tjweb/index
     *
     * 银联沙箱环境
     * 成为银联开发者之后，会自动得到沙箱环境，但是这个沙箱环境，开发的时候并不会用到。
     *
     *
     * 使用商户入网测试案例
     * 银联为商户提供了入网测试案例和接口，我们可以轻松实现支付功能
     * https://open.unionpay.com/tjweb/user/mchTest/index
     *
     * 下载数字证书文件
     * 调用测试案例需要使用很多数字证书，大家要从课程的git项目中，把数字证书下载到本地
     *
     *
     * 安装花生壳软件
     * 为了接收银联系统的回调请求和通知消息，我们需要把动态IP转换成静态域名
     * 花生壳软件的内网穿透功能，我们可以在外网用静态域名访问到本地主机
     * https://hsk.oray.com/download/
     */


    /**
     * 安卓APP银联支付流程
     *
     * 银联支付的优势
     * （1）大额支付，银联更具备优势。例如交通银行银行卡，使用U盾的情况下，允许用户每日支付的上限为100万元
     * （2）银联支付的可追索性，比微信支付和支付宝好很多
     *
     * 特殊说明
     * （1）银联支持的移动端支付只有两种:苹果支付和安卓支付
     * （2）苹果支付只支持iOS系统，安卓支付只能运行在安卓系统上面，但是Android Pay服务不能在中国境内使用
     *
     */




    @Autowired
    private SDKConfig config;

    @Autowired
    private AcpService acpService;

    @Value("${unionpay.mch-id}")
    private String mchId;

    @Value("${unionpay.frontUrl}")
    private String frontUrl;

    @Value("${unionpay.backUrl}")
    private String backUrl;

    @Value("${unionpay.orderUrl}")
    private String orderUrl;

    @Value("${unionpay.wapFrontUrl}")
    private String wapFrontUrl;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private UserService userService;

    @Autowired
    private OrderService orderService;

    @Autowired
    private JwtUtils jwtUtils;

    // Wap付款
    @PostMapping("/wapPayOrder")
    public ResponseResult wapPayOrder(@RequestBody PayOrderParam param, @RequestHeader HashMap header) {
        /**
         * 封装创建支付订单方法
         *
         * 银联SDK程序包
         * 银联只提供了案例项目，需要我们自己剥离SDK程序，但是你可以从课程git项目中下载已经处理好的SDK程序包
         */

        String token = header.get("token").toString();
        Long userId = Long.parseLong(jwtUtils.getClaimByToken(token).getSubject());
        int orderId = param.getOrderId();
        TbUser user = new TbUser();
        user.setUserId(userId);
        long count = userService.getCount(user);
        //判断用户是否存在
        if (count == 0) {
            return ResponseResult.error("用户不存在");
        }

        //判断用户是否拥有这个订单，并且订单是未支付状态
        TbOrder order = new TbOrder();
        order.setUserId(userId.intValue());
        order.setId(orderId);
        order.setStatus(1);
        count = orderService.getCount(order);
        if (count == 0) {
            return ResponseResult.error("不是有效的订单");
        }

        // todo 验证购物券是否有效
        // todo 验证团购活动是否有效

        //查询订单详情信息
        order = new TbOrder();
        order.setId(orderId);
        order = orderService.getOne(order); //订单对象
        //向银联平台发出请求，创建支付订单
        String amount = order.getAmount().multiply(new BigDecimal("100")).intValue() + ""; //微信‘银联最小单位是分，不能有小数点，所以金额*100

        // 接入地址 https://open.unionpay.com/tjweb/acproduct/list?apiSwcldm=453&index=2
        try {
            Map<String, String> requestData = new HashMap<String, String>();
            requestData.put("version", "5.0.0");    //版本号，全渠道默认值
            requestData.put("encoding", "UTF-8");  //字符集编码，可以使用UTF-8,GBK两种方式
            requestData.put("signMethod", "01"); //签名方法
            requestData.put("txnType", "01");  //交易类型 ，01：消费
            requestData.put("txnSubType", "01");  //交易子类型， 01：自助消费
            requestData.put("bizType", "000201");  //业务类型，B2C网关支付，手机wap支付
            requestData.put("channelType", "07");  //渠道类型；07：PC,平板  08：手机
            requestData.put("merId", mchId);       //商户号码
            requestData.put("accessType", "0");    //接入类型，0：直连商户
            requestData.put("orderId", order.getCode());  //商户订单号
            //订单发送时间，取系统时间，格式为YYYYMMDDhhmmss，必须取当前时间，否则会报txnTime无效
            requestData.put("txnTime", new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()));
            requestData.put("currencyCode", "156");  //交易币种（境内商户一般是156 人民币）
            requestData.put("txnAmt", amount);  //交易金额，单位分，不要带小数点
            requestData.put("frontUrl", wapFrontUrl);
            requestData.put("backUrl", backUrl);
            requestData.put("payTimeout", new SimpleDateFormat("yyyyMMddHHmmss").format(new Date().getTime() + 15 * 60 * 1000));
            //数字签名
            Map<String, String> submitFromData = acpService.sign(requestData, "UTF-8");
            String requestFrontUrl = config.getFrontRequestUrl();
            String htmlCode = AcpService.createAutoFormHtml(requestFrontUrl, submitFromData, "UTF-8");

            /**
             * 缓存HTML代码
             *
             * 下载Redis服务器
             * （1）由于Redis 4.0之后，很多功能只支持Linux系统，所以Windows上面的Redis最高版本只有3.2.10
             * （2）大家可以从本课程的git项目中下载到Windows版本的Redis压缩包，解压缩就可以使用
             * （3）Redis程序路径中不能含有中文或者空格，否则会闪退
             *
             * 修改Redis配置文件
             * 打开redis配置文件，需要修改下面这些参数
             * bind0.0.0.0
             * port 6379
             * databases 4
             * requirepass abc123456
             * maxmemory 100M
             *
             *
             */
            if (htmlCode != null && htmlCode.length() > 0) {
                order.setPaymentType(3);
                order.setId(order.getId());// 查询条件
                orderService.update(order);

                WapUnionPay wapUnionPay = new WapUnionPay();
                wapUnionPay.setUserId(userId + "");
                wapUnionPay.setOrderId(orderId + "");
                wapUnionPay.setHtmlCode(htmlCode);

                redisUtils.set(order.getCode(), wapUnionPay, 15 * 60 * 1000);
                return ResponseResult.success("code", order.getCode());
            } else {
                return ResponseResult.error("支付订单创建失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseResult.error("银联支付模块故障");
        }

    }

    /**
     * 接收后端通知消息
     *
     */
    // 接收银联付款通知
    @RequestMapping("/recvBackResponse")
    public void recvBackResponse(HttpServletRequest request, HttpServletResponse response) throws Exception {
        request.setCharacterEncoding("UTF-8");
        HashMap<String, String> reqParam = new HashMap<>();
        Enumeration<String> enumeration = request.getParameterNames();
        while (enumeration.hasMoreElements()) {
            String name = enumeration.nextElement();
            String value = request.getParameter(name);
            reqParam.put(name, value);
        }
        Map<String, String> valideData = null;
        if (!reqParam.isEmpty()) {
            Iterator<Map.Entry<String, String>> it = reqParam.entrySet().iterator();
            valideData = new HashMap<String, String>(reqParam.size());
            while (it.hasNext()) {
                Map.Entry<String, String> e = it.next();
                String key = (String) e.getKey();
                String value = (String) e.getValue();
                value = new String(value.getBytes("UTF-8"), "UTF-8");
                valideData.put(key, value);
            }
        }
        //重要！验证签名前不要修改reqParam中的键值对的内容，否则会验签不过

        if (!acpService.validate(valideData, "UTF-8")) {
            //验签失败，需解决验签问题
            System.out.println("签名验证失败");
            response.setCharacterEncoding("UTF-8");
            Writer writer = response.getWriter();
            writer.write("签名验证失败");
            writer.close();
        } else {
            System.out.println("后端通知签名验证成功");

            String respCode = request.getParameter("respCode");
            String queryId = request.getParameter("queryId");
            String code = request.getParameter("orderId");
            if ("00".equals(respCode)) {
                TbOrder tbOrder = new TbOrder();
                tbOrder.setCode(code); // 查询条件
                tbOrder.setStatus(2);
                tbOrder.setPaymentType(3);
                tbOrder.setPrepayId(queryId);
                orderService.update(tbOrder);
            }
            response.setCharacterEncoding("UTF-8");
            Writer writer = response.getWriter();
            writer.write("ok");
            writer.close();
        }
    }

    /**
     * 前端回调页面
     *
     * https://open.unionpay.com/tjweb/acproduct/APIList?apiservId=453&acpAPIId=334&bussType=0
     *
     *
     */
    // 接收银联页面跳转请求
    @RequestMapping("/recvWapFrontResponse")
    public void recvWapFrontResponse(HttpServletRequest request, HttpServletResponse response) throws Exception {
        request.setCharacterEncoding("UTF-8");
        HashMap<String, String> reqParam = new HashMap<>();
        Enumeration<String> enumeration = request.getParameterNames();
        while (enumeration.hasMoreElements()) {
            String name = enumeration.nextElement();
            String value = request.getParameter(name);
            reqParam.put(name, value);
        }

        Map<String, String> valideData = null;
        String htmlCode = null;
        if (!reqParam.isEmpty()) {
            Iterator<Map.Entry<String, String>> it = reqParam.entrySet().iterator();
            valideData = new HashMap<String, String>(reqParam.size());
            while (it.hasNext()) {
                Map.Entry<String, String> e = it.next();
                String key = (String) e.getKey();
                String value = (String) e.getValue();
                valideData.put(key, value);
            }
        }

        if (!acpService.validate(valideData, "UTF-8")) {
            htmlCode = "<html><body><h1>数据签名错误<h1></body></html>";
        } else {
            System.out.println("前端回调签名验证成功");

            String respCode = request.getParameter("respCode");
            String queryId = request.getParameter("queryId");
            String code = request.getParameter("orderId");
            if ("00".equals(respCode)) {
                TbOrder tbOrder = new TbOrder();
                tbOrder.setCode(code); // 查询条件
                tbOrder.setStatus(2);
                tbOrder.setPaymentType(3);
                tbOrder.setPrepayId(queryId);
                orderService.update(tbOrder);
                htmlCode = "<html><body><h1>商户已经接收到付款成功通知<h1></body></html>";
            } else {
                htmlCode = "<html><body><h1>付款失败<h1></body></html>";
            }
            response.setCharacterEncoding("UTF-8");
            Writer writer = response.getWriter();
            writer.write(htmlCode);
            writer.close();
        }
    }

    // 生成银联支付的跳转页面
    @RequestMapping("/htmlCodePage")
    public void htmlCodePage(HttpServletRequest request, HttpServletResponse response) throws Exception {
        String code = request.getParameter("code");
        WapUnionPay wapUnionPay = redisUtils.get(code, WapUnionPay.class);
        String htmlCode = wapUnionPay.getHtmlCode();
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html");
        Writer writer = response.getWriter();
        writer.write(htmlCode);
        writer.close();
    }


}